home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_MacPaint / Source / shared.subproj / ConvertController.m < prev    next >
Text File  |  1995-06-12  |  59KB  |  1,574 lines

  1. /***********************************************************************\
  2. Common class for subclass Controller in all Convert programs
  3. Copyright (C) 1993 David John Burrowes
  4.  
  5. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
  6.  
  7. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  8.  
  9. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10.  
  11. The author, David John Burrowes, can be reached at:
  12.     davidjohn@kira.net.netcom.com
  13.     David John Burrowes
  14.     1926 Ivy #10
  15.     San Mateo, CA 94403-1367
  16. \***********************************************************************/
  17.  
  18. #import "ConvertController.h"
  19. #import "File.h"
  20. #import  "AbstractConverter.h"        // mainly to stop annoying 'I don't know about this runtime method call'
  21.  
  22. #import <stdio.h>
  23. #import <stdlib.h>
  24. #import <string.h>
  25. #import <appkit/Panel.h> 
  26. #import <appkit/Window.h>        // for manipulations of ProgressWindow
  27. #import <appkit/Matrix.h>
  28. #import <appkit/Cell.h>
  29. #import <appkit/SavePanel.h>
  30. #import <appkit/OpenPanel.h>
  31. #import <libc.h>                //for getwd
  32. #import <sys/param.h>
  33. //
  34. //    Include the following 2 for drag and drop facility, plus for IAC communications.
  35. //
  36. #import "ConvertListener.h"
  37. #import "ConvertSpeaker.h"
  38. #import <appkit/Application.h>        // To get NXApp (I thought that is what defaults was...)
  39. #import <appkit/publicWraps.h>    // for NXConvertWinNumToGlobal
  40. #import <appkit/graphics.h>        // For NXPing
  41. #import <appkit/publicWraps.h>     //    For NXBeep
  42.  
  43. @implementation ConvertController
  44.  
  45. /*============================================================*\
  46.  
  47.     The following routines are all for the application delegate responsibilities of this
  48.     class.  Basically dealing with starting up, shutting down, and with allowing the
  49.     user to double click on a file in the workspace and have it open up, and setting up
  50.     for providnig communications between applications
  51.  
  52. \*============================================================*/
  53.  
  54. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  55. //    Routine:        appWillInit:
  56. //    Parameters:    our caller
  57. //    Returns:        self
  58. //    Stores:        none
  59. //    Description:
  60. //        This is used to store a specialized speaker and listener in the application.
  61. //        these specialized objects are subclasses of the normal speaker and listener
  62. //        classes, and together implement a method to allow the caller to instruct this
  63. //        class to convert a file.
  64. //    Bugs:
  65. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  66. - appWillInit: sender
  67. {
  68.     Instance        mySpeaker;
  69.     Instance        myListener;
  70.     //
  71.     //    Set up the conversion-aware speaker
  72.     //
  73.     mySpeaker = [[ConvertSpeaker alloc] init];
  74.     [mySpeaker setDelegate:NXApp];
  75.     [NXApp setAppSpeaker:mySpeaker];
  76.     //
  77.     //    Set up the conversion-aware listener
  78.     //
  79.     myListener = [[ConvertListener alloc] init];
  80.     [myListener setDelegate:NXApp];
  81.     [NXApp setAppListener:myListener];
  82.     
  83.     return self;
  84. }
  85.  
  86.  
  87. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. //    Routine:        appDidInit:
  89. //    Parameters:    our caller
  90. //    Returns:        self
  91. //    Stores:        none
  92. //    Description:
  93. //        This is for when this class acts as a delegate to the application.  It allows it to
  94. //        do some final initialization stuff.  The big thins is to allow us to register out
  95. //        window so the user will be able to drag icons over it, and thus serve as the
  96. //        drag-and-drop facilities that we support.  This later causes iconEntered: and
  97. //        iconReleasedAt::ok: messages to be sent later.
  98. //        The 2.x code for the drag-and-drop was mainly copied from DrawDocument.
  99. //        The 3.x code is copied straight from NeXT's Window.rtf help doc.   The window
  100. //        registration for 3.0 is much more straight forward.  
  101. //    Bugs:
  102. //        Perhaps I should be using appDidBecomeActive here some?
  103. //    History
  104. //        93.01.24    djb    Modified so will make use of NS 3.0 drag and drop
  105. //        93.07.16    djb    Added services delegate call for filtering (and boy did it take me
  106. //                    an embarassingly long time to figure out that I needed this.  duhhh.. =)
  107. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  108. - appDidInit: sender
  109. {
  110. #if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
  111.     //
  112.     //    Allocate a listener instance, and store as one of our instance variables.
  113.     //    Set it up as using our private port, and have the app's speaker register
  114.     //    the window and port.
  115.     //
  116.     listener = [[Listener allocFromZone:[self zone]] init]; // from MY zone?
  117.     [listener setDelegate:self];    // We'll now get the messages from the workspace
  118.     [listener usePrivatePort];
  119.     [listener addPort];
  120. #endif
  121.     [self   allowDragAndDrop];    // do nothing special for NS 3.0 style
  122.     //
  123.     //    Force the window the front, and give it's miniwindow an icon.
  124.     //
  125.     [ProgressWindow setMiniwindowIcon: "Application"];
  126.     [ProgressWindow makeKeyAndOrderFront:self];
  127.     //
  128.     //    93.01.08    djb    Whoops.  Wasn't disabling parts of edit menu right off as needs to be
  129.     //
  130.     [cutCommand        setEnabled: NO];
  131.     [pasteCommand        setEnabled: NO];
  132.     [spellingCommand    setEnabled: NO];
  133.     [checkSpellingCommand    setEnabled: NO];
  134.     //
  135.     //    93.07.18    djb    Added this so any subclasses doing filtering stuff (or serives stuff)
  136.     //                will be happy
  137.     //
  138.     [[NXApp appListener] setServicesDelegate:self];
  139.     
  140.     return self;
  141. }
  142.  
  143.  
  144. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  145. //    Routine:        appWillTerminate:
  146. //    Parameters:    our caller
  147. //    Returns:        self
  148. //    Stores:        none
  149. //    Description:
  150. //        This serves tobalance the init above!
  151. //        Mainly, it unregisters our window information that was used to allow the
  152. //            workspace to have the user drag folders over our window!
  153. //        This code is basically copied directly from DrawDocument.m  =)
  154. //    Bugs:
  155. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  156. - appWillTerminate: sender
  157. {
  158.     [self   refuseDragAndDrop];
  159.     if (listener != NullInstance)
  160.         [listener   free];
  161.     return self;
  162. }
  163.  
  164.  
  165. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. //    Routine:        appAcceptsAnotherFile:
  167. //    Parameters:    our caller
  168. //    Returns:        self
  169. //    Stores:        none
  170. //    Description:
  171. //        If we can accept another file to be opened, we return YES.  If we can't we respond
  172. //        NO.  For now, we always return that we'll accept another file.  This just ends
  173. //        up queueing files waiting to be converted.
  174. //    Bugs:
  175. //        To be honest, this makes me a bit uncomfortable.  I feel as though I should be
  176. //        keeping some kinda lock and returning NO if I'm presently converting
  177. //        something.  yet, if I'm converting something, nothing else should be able to
  178. //        be doing any execution here, so I guess it doesn't matter.  
  179. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  180.  
  181. - (BOOL)appAcceptsAnotherFile:sender
  182. {
  183.         return YES;
  184. }
  185.  
  186.  
  187. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  188. //    Routine:        app:openFile:type:
  189. //    Parameters:    The name of the file to be converted
  190. //                It's extension
  191. //    Returns:        a flag indicating whether we can deal with the file
  192. //    Stores:        none
  193. //    Description:
  194. //        The user double clicked on a file in the workspace, and now we are asked to open it.
  195. //        All this does, though, is pass the file information on to the first level conversion
  196. //        method and let it try to deal with it.
  197. //    Bugs:
  198. //        Might it be nicer to play a game, like below, where we do a 'perform' to do the
  199. //        conversion after we return YES to the caller?  That begins to seem a bit
  200. //        dangerous to me.  still.....
  201. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  202.  
  203. - (int)app:sender openFile:(const char *)filename type:(const char *)aType
  204. {
  205.     CString    tempString;
  206.     //
  207.     //    Ask the convert controlling object to convert this particular file
  208.     //
  209.     [self   ConvertThisFile: filename];
  210.     //
  211.     //    Check if the file was converted alright...
  212.     //
  213.     if ([self   GetErrorCode] == ERR_OK)
  214.     {
  215.         [SourceTitle    setStringValue: "Converted:"];
  216.         [Status    setStringValue: "The conversion is done."];
  217.         [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  218.     }
  219.     else
  220.     {
  221.         [SourceTitle    setStringValue: "Failed:"];
  222.         tempString = [self   GetErrorText];
  223.         [Status    setStringValue: tempString];
  224.         FreeCString(tempString);
  225.     }
  226.  
  227.     return YES;    // Always report that it was converted ok
  228. }
  229.  
  230.  
  231. /*============================================================*\
  232.  
  233.     The following routines are all for the drag-and-drop facilities which allow the
  234.     user to drag files from the workspace, and drop them over the main window of
  235.     the application.  Fun with InterProcessCommuniction. =)
  236.  
  237. \*============================================================*/
  238.  
  239.  
  240. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  241. //    Routine:        iconEntered:at:iconWindow:iconX:iconY:iconWidth:iconHeight:pathList:
  242. //    Parameters:    various, mostly not important at the moment
  243. //    Returns:        0 if successfull, something else if not
  244. //    Stores:        none
  245. //    Description:
  246. //        When the user drags a clump of one of more files over our window, we get
  247. //        this message.  We store away the list of files, because we are not given it
  248. //        later on.  =(  Note that we first free any pending set of file paths in the
  249. //        extremely unlikely condition that there is one already there, and then
  250. //        copy the list of paths for use in iconReleasedAt: below
  251. //    Bugs:
  252. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  253. - (int)iconEntered:(int)windowNum at:(double)x :(double)y
  254.     iconWindow:(int)iconWindowNum iconX:(double)iconX iconY:(double)iconY
  255.     iconWidth:(double)iconWidth iconHeight:(double)iconHeight
  256.     pathList:(char *)pathList
  257. {
  258.     if (filePaths != NullCString)
  259.         FreeCString(filePaths);
  260.     filePaths = NewCString(strlen(pathList));
  261.     strcpy(filePaths, pathList);
  262.     return 0;
  263. }
  264.  
  265.  
  266. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  267. //    Routine:        iconMovedTo::
  268. //    Parameters:    n/a
  269. //    Returns:        n/a
  270. //    Stores:        n/a
  271. //    Description:
  272. //        I'm NOT implimenting this, though it might still be nice if we wanted some
  273. //        kinda graphical effect, or if we could enforce dropping in only a particular
  274. //        area (I like this idea, rather than the generic droppoing we have now...
  275. //        (heh.  drop on the NeXT icon to convert it TO NeXt, drop it on the Mac
  276. //        to convert TO mac.  =)  However, the below code demonstrates that one can
  277. //        not change the little-copy-cursor-icon-thingie based on a return value
  278. //    Bugs:
  279. //        None, of course, since it is commented out, unless you count that I've grotesquely put it
  280. //        all on one line so one doesn't mistake that it is in fact commented out for now.
  281. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  282. /*- (int)iconMovedTo:(double)x :(double)y { if ((x > 100) || (y > 100)) {NXBeep(); return 0;} else  return 1; } */
  283.  
  284.  
  285.  
  286. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  287. //    Routine:        iconReleasedAt::ok:
  288. //    Parameters:    x and y coords of the dropping point
  289. //    Returns:        0 if successfull, something else if not
  290. //                a ptr to a boolean flag returned by reference
  291. //    Stores:        none
  292. //    Description:
  293. //        When the user droppes the files, we inform the workspace manager that we
  294. //        can deal with them (always).  We then ask ourselves to do something in
  295. //        some fraction of a second.
  296. //        The reason for all this is simple: processing the files is apt to take some time,
  297. //        and we don't want that icon hanging on the window while we do so.  So,
  298. //        we gotta return soon.  Fact is, we also want to be liberal about what kind
  299. //        of files to accept, since in general many of these conversion programs may not
  300. //        have files with 'proper' extensions (e.g. what isthe extension for a Mac font?).
  301. //        So, we tell the workspace all is well, so the icon goes away.  Soon, we get
  302. //        reminded to ProcessDroppedFiles, which will go through and actually start
  303. //        the processing.
  304. //    Bugs:
  305. //        As with some of the routines above, a bit of me is concerned that if this code is
  306. //        executed while this instance is also doing some conversion, trouble will result.
  307. //        but, then, of course, this isn't a multi-threaded thing, so I shouldn't need to
  308. //        worry about that.  Why does my mind keep trying to think in terms of a
  309. //        processing architecture that doesn't exist yet?
  310. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  311. - (int)iconReleasedAt:(double)x :(double)y ok:(int *)flag
  312. {
  313.  
  314.     [self   perform: @selector(ProcessDroppedFiles)
  315.         with: self
  316.         afterDelay: 100
  317.         cancelPrevious: NO];
  318.     *flag = YES;
  319.     return 0;
  320. }
  321.  
  322.  
  323. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  324. //    Routine:        iconExitedAt::
  325. //    Parameters:    The x and y coordinates at which the icon left the window
  326. //    Returns:        0 if successfull, something else if not
  327. //    Stores:        none
  328. //    Description:
  329. //        If the user drags the icon off the window, we just free the string of
  330. //        paths and reutrn.
  331. //    Bugs:
  332. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  333. - (int)iconExitedAt:(double)x :(double)y
  334. {
  335.     if (filePaths != NullCString)
  336.     {
  337.         FreeCString(filePaths);
  338.         filePaths = NullCString;
  339.     }
  340.     return 0;
  341. }
  342.  
  343.  
  344. #if (NSmajor == 3)
  345.  
  346. /*============================================================*\
  347.  
  348.     The following routines are NeXTSTEP 3.0 routines that will allow for drag and drop
  349.     of filenames.  NOTE that these are equivalent to the routines above, and that both
  350.     are being maintained so I can recompile easily under 2.1 if needed.
  351.     NOTE!  For this to work, the instance that this is must be designated the delagate
  352.     of the progresswindow!
  353.     It Might be more efficient, by the way, instead of registering and undregistering the
  354.     accepted types all the time, to instead set a flag and return NX_DragOperationNone.
  355.  
  356. \*============================================================*/
  357.  
  358. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  359. //    Routine:        draggingEntered:
  360. //    Parameters:    the caller
  361. //    Returns:        NX_DragOperationCopy
  362. //    Stores:        none
  363. //    Description:
  364. //        Aside from telling the caller that we'll happily convert whatever files we're
  365. //        being asked about, this also gets and stores the tab delimited list of file names
  366. //        for potential later processing.
  367. //    Bugs:
  368. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  369. - (NXDragOperation)draggingEntered:(id)sender
  370. {
  371.     Instance    filenamePasteboard;
  372.     CString    names;
  373.     int        thelength;
  374.     filenamePasteboard = [sender  draggingPasteboard];
  375.  
  376.     if (filePaths != NullCString)
  377.         FreeCString(filePaths);
  378.     [filenamePasteboard   readType: NXFilenamePboardType data: &names
  379.             length: &thelength];
  380.     filePaths = NewCString(thelength);
  381.     strcpy(filePaths, names);
  382.     [filenamePasteboard   deallocatePasteboardData: names length: thelength];
  383.  
  384.     return NX_DragOperationCopy;
  385. }
  386.  
  387.  
  388. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  389. //    Routine:        draggingUpdated:
  390. //    Parameters:    the caller
  391. //    Returns:        NX_DragOperationCopy
  392. //    Stores:        none
  393. //    Description:
  394. //        This merely indicates that we'll be happy to try to convert the specified
  395. //        filenames.  We aren't particular aboout what kinds of files they are. =)
  396. //    Bugs:
  397. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  398. - (NXDragOperation)draggingUpdated:(id)sender
  399. {
  400.     return NX_DragOperationCopy;
  401. }
  402.  
  403. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  404. //    Routine:        prepareForDragOperation:
  405. //    Parameters:    the caller
  406. //    Returns:        YES
  407. //    Stores:        none
  408. //    Description:
  409. //        We will always do the drag operation.
  410. //    Bugs:
  411. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  412. - (BOOL)prepareForDragOperation:(id)sender
  413. {
  414.     return YES;
  415. }
  416.  
  417.  
  418. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  419. //    Routine:        performDragOperation:
  420. //    Parameters:    the caller
  421. //    Returns:        YES
  422. //    Stores:        none
  423. //    Description:
  424. //        Convert the file(s).  Indicate that we could.
  425. //    Bugs:
  426. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  427.  - (BOOL)performDragOperation:(id)sender
  428. {
  429.     [self   perform: @selector(ProcessDroppedFiles)
  430.         with: self
  431.         afterDelay: 100
  432.         cancelPrevious: NO];
  433.     return YES;
  434. }
  435.  
  436. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  437. //    Routine:        concludeDragOperation:
  438. //    Parameters:    the caller
  439. //    Returns:        self
  440. //    Stores:        none
  441. //    Description:
  442. //        Do nothing, as there's nothing to clean up, really (we could free the copy of
  443. //        the pathnames, but there's no real urgent need).
  444. //    Bugs:
  445. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  446. - concludeDragOperation:(id)sender
  447. {
  448.     return self;
  449. }
  450.  
  451. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  452. //    Routine:        draggingExited:
  453. //    Parameters:    the caller
  454. //    Returns:        returns self
  455. //    Stores:        none
  456. //    Description:
  457. //        If the user drags the icon off the window, we just free the string of
  458. //        paths and reuturn
  459. //    Bugs:
  460. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  461. - draggingExited:(id)sender
  462. {
  463.     if (filePaths != NullCString)
  464.     {
  465.         FreeCString(filePaths);
  466.         filePaths = NullCString;
  467.     }
  468.     return self;
  469. }
  470.  
  471. #endif
  472.  
  473.  
  474. /*============================================================*\
  475.  
  476.     The following routines are various basic utility routines used by  the above
  477.     methods.  In short:  toggle drag-and-drop on and off,  and process the file names
  478.     when/if they are actually dropped.
  479.  
  480. \*============================================================*/
  481.  
  482.  
  483. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  484. //    Method:        refuseDragAndDrop
  485. //    Parameters:    none
  486. //    Returns:        self
  487. //    Stores:        none
  488. //    Description:
  489. //        This handy method is used to turn off our request to allow files to be dropped
  490. //        over the application's window.  Clearly, it has conditionally compiled sections
  491. //        for NS 3.0 and 2.x style drag and drop
  492. //    Bugs:
  493. //    History:
  494. //        93.01.24    djb    Added support for 3.0 style drag and drop
  495. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  496.  
  497. - refuseDragAndDrop
  498. {
  499. #if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
  500.     unsigned int        windowNum;
  501.     Instance            speaker = [NXApp appSpeaker];
  502.  
  503.     if (listener != NullInstance)
  504.     {
  505.         [speaker   setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  506.         NXConvertWinNumToGlobal([ProgressWindow   windowNum], &windowNum);
  507.         [speaker   unregisterWindow: windowNum];
  508.     }
  509. #else
  510.     [ProgressWindow   unregisterDraggedTypes];
  511. #endif
  512.  
  513.     return self;
  514. }
  515.  
  516.  
  517. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  518. //    Method:        allowDragAndDrop
  519. //    Parameters:    none
  520. //    Returns:        self
  521. //    Stores:        none
  522. //    Description:
  523. //        This turns on the ability to have files droped over our window so we can
  524. //        process them.   It has code fro both NS 2.x and 3.x style implementations
  525. //    Bugs:
  526. //    History:
  527. //        93.01.24    djb    Added support for 3.0 style drag and drop
  528. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  529.  
  530. - allowDragAndDrop
  531. {
  532. #if ((NSmajor == 2) || (DoNS3DragNDrop == 0))
  533.     unsigned int        windowNum;
  534.     Instance            speaker = [NXApp appSpeaker];
  535.  
  536.     NXConvertWinNumToGlobal([ProgressWindow   windowNum], &windowNum);
  537.     [speaker   setSendPort: NXPortFromName(NX_WORKSPACEREQUEST, NULL)];
  538.     [speaker   registerWindow:windowNum toPort:[listener   listenPort]];
  539. #else
  540.     //
  541.     //    We must tell the window to be ready for some dragged file names
  542.     //
  543.     const char *dataType[] = {NXFilenamePboardType};
  544.     [ProgressWindow   registerForDraggedTypes: dataType   count:1];
  545. #endif
  546.  
  547.     return self;
  548. }
  549.  
  550.  
  551.  
  552. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  553. //    Routine:        ProcessDroppedFiles
  554. //    Parameters:    none
  555. //    Returns:        self
  556. //    Stores:        none
  557. //    Description:
  558. //        There is, presumably,  string of file names waiting to be processed.  We
  559. //        should process them one at a time, then.  We make a copy of the file name
  560. //        list, and then repeatedly  locate the and start and end of each file path name
  561. //        in the list (they're tab delimited).  For each that we find, we process it.
  562. //    Bugs:
  563. //        We could almost certainly munge the file path string directly, but I feel
  564. //        safer treating it 'right'.
  565. //        If any of the files have tabs in their names, I have no idea what will or will
  566. //        have happened.
  567. //        I chose not to nuke the filePaths at when done here.  This might not be good?
  568. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  569. - ProcessDroppedFiles
  570. {
  571.     CString    allpaths,
  572.             nextpath,
  573.             location;
  574.     CString    tempString;
  575.     
  576.     allpaths = NewCString(strlen(filePaths));
  577.     strcpy(allpaths, filePaths);
  578.     nextpath = allpaths;
  579.     do
  580.     {
  581.         //
  582.         //    Find the next tab.  If we found it, put a null there.  If we did not,
  583.         //    then this is the end of allpaths.  We still have the last file name to
  584.         //    process, so we keep going.  In all cases, convert the file, set up for
  585.         //    the next conversion (if any).  Check whether we reached the end, and
  586.         //    if so quit.
  587.         //
  588.         location = strchr(nextpath, '\t');
  589.         if (location != EndOfCString)
  590.             location[0] = EndOfCString;
  591.         [self   ConvertThisFile: nextpath];
  592.         //
  593.         //    Check if the file was converted alright...
  594.         //
  595.         if ([self   GetErrorCode] == ERR_OK)
  596.         {
  597.             [SourceTitle    setStringValue: "Converted:"];
  598.             [Status    setStringValue: "The conversion is done."];
  599.             [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  600.         }
  601.         else
  602.         {
  603.             [SourceTitle    setStringValue: "Failed:"];
  604.             tempString = [self   GetErrorText];
  605.             [Status    setStringValue: tempString];
  606.             FreeCString(tempString);
  607.         }
  608.         nextpath = location+1;
  609.     }
  610.     while (location != EndOfCString);
  611.     FreeCString(allpaths);
  612.     return self;
  613. }
  614.  
  615.  
  616.  
  617. /*============================================================*\
  618.  
  619.     The following methods are intended to be used to allow a limited dialog as the
  620.     'slave' of another app that wants us to convert a file.
  621.  
  622. \*============================================================*/
  623.  
  624.  
  625. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  626. //    Routine:        msgQuit
  627. //    Parameters:    An integer that we fill in YES if we work (we always work =)
  628. //    Returns:        self
  629. //    Stores:        none
  630. //    Description:
  631. //        This will set up a delayed message to the app to have it quit.  We allow PLENTY
  632. //        of time for us to quit and retun to the caller before that method gets invoked.  If
  633. //        we didn't provide enough time (as, for instance the Draw demo app does not), then
  634. //        the caller will hang until time out because we would never return from the call.
  635. //    Bugs:
  636. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  637. - (int)msgQuit:(int *)flag
  638. {
  639.     [NXApp   perform:@selector(terminate)  with: NXApp  afterDelay: 300
  640.         cancelPrevious: NO];
  641.     *flag = YES;
  642.     return 0;
  643. }
  644.  
  645.  
  646. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  647. //    Routine:        msgConvert:To:
  648. //    Parameters:    The name of the file to convert from, and the name of the file to convert to.
  649. //    Returns:        self
  650. //    Stores:        none
  651. //    Description:
  652. //        This provides a way for an other application to call us, and get into our
  653. //        main conversion routines below.  This takes the two files, opens them, and jumps
  654. //        to the main conversio method.  If either fails to open, we abort the process.
  655. //        However, the caller never learns of this.
  656. //        These file names, basically, should be complete pathnames
  657. //    Bugs:
  658. //        I still don't understand how we we can be sure that these strings haven't been
  659. //        freed by the time we get to them...
  660. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  661. - msgConvert: (char *) sourceFile  To: (char*) destFile
  662. {
  663.     CString    sourceName = NewCString(strlen(sourceFile));
  664.     CString    destName = NewCString(strlen(destFile));
  665.     CString    dirname;
  666.     CString    filename;
  667.     CString    tempString;
  668.     Instance    source;
  669.     Instance    destination;
  670.  
  671.     strcpy(sourceName, sourceFile);
  672.     strcpy(destName, destFile);
  673.     //
  674.     //    Get ourselves out of the way of the caller.
  675.     //    Bug: We still display our window briefly.
  676.     //
  677.     [NXApp    hide: self];
  678.     
  679.     source = [self openSourceFile: sourceName];
  680.     if ([self   GetErrorCode] == ERR_OK)
  681.     {
  682.         [StatusTitle    setStringValue: "Status:"];
  683.         [Status    setStringValue: "Converting"];
  684.         [SourceTitle    setStringValue: "Converting:"];
  685.         [SourcePathTitle    setStringValue: "In:"];
  686.         filename = [source GetFilename];
  687.         dirname = [source GetDirectory];
  688.         [SourceFileName    setStringValue: filename];
  689.         [SourcePath        setStringValue: dirname];
  690.         FreeCString (filename);
  691.         FreeCString (dirname);
  692.  
  693.         destination = [self openDestFile: destName];
  694.         if ([self   GetErrorCode] == ERR_OK)
  695.         {
  696.             [DestTitle    setStringValue: "To:"];
  697.             [DestPathTitle    setStringValue: "In:"];
  698.  
  699.             filename = [destination GetFilename];
  700.             dirname = [destination GetDirectory];
  701.             [DestFileName    setStringValue: filename];
  702.             [DestPath        setStringValue: dirname];
  703.             FreeCString (filename);
  704.             FreeCString (dirname);
  705.             [self   DoConversionFrom: source  To: destination];
  706.             if ([self   GetErrorCode] == ERR_OK)
  707.             {
  708.                 [SourceTitle    setStringValue: "Converted:"];
  709.                 [Status    setStringValue: "The conversion is done."];
  710.             }
  711.             else
  712.             {
  713.                 [SourceTitle    setStringValue: "Failed:"];
  714.                 tempString = [self   GetErrorText];
  715.                 [Status    setStringValue: tempString];
  716.                 FreeCString(tempString);
  717.             }
  718.         }
  719.         else
  720.         {
  721.             [SourceTitle    setStringValue: "Failed:"];
  722.             [Status    setStringValue: "Could not open the destination file."];
  723.             [source   free];
  724.         }
  725.     }
  726.     else
  727.     {
  728.         [SourceTitle    setStringValue: "Failed:"];
  729.         [Status    setStringValue: "Could not open the source file."];
  730.     }
  731.     FreeCString(sourceName);
  732.     FreeCString(destName);
  733.  
  734.     return 0;
  735. }
  736.  
  737.  
  738.  
  739. /*============================================================*\
  740.  
  741.     The rest of the methods here do the stuff that is the focus of the application.
  742.     Specifically, we init, deal with user preference settings, set the indicator on the
  743.     screen to indicate how far done the conversion is, open and close files and check
  744.     that the file to be converted is OK.
  745.  
  746. \*============================================================*/
  747.  
  748.  
  749. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  750. //    Routine:        init
  751. //    Parameters:    none
  752. //    Returns:        self
  753. //    Stores:        none
  754. //    Description:
  755. //        This overrides the defalut init method.  But it does little else.
  756. //        Other initializations done in AppDidInit, above...
  757. //        Note that subclasses should OVERRIDE this 
  758. //    Bugs:
  759. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  760. - init
  761. {
  762.     CString    temppath,
  763.             fullpath,
  764.             tempPos;
  765.             
  766.     [super init];
  767.     destIsDead = NO;
  768.     //
  769.     //    Clear, if necessary, the string of file names
  770.     //
  771.     filePaths = NullCString;
  772.     //
  773.     //    Provide silly defaul strings.
  774.     //
  775.     ConversionString = "Converting a file to another";
  776.     SourcePrompt = "a file:";
  777.     DestPrompt = "another file:";
  778.     DestExtension = ".a";
  779.     DefaultsOwner = "I have no defaults";
  780.     //
  781.     //    Locate the path where the application exists (if the app is a foo.app type appwrapper,
  782.     //    this includes the foo.app directory).
  783.     //    (if NXArgv doesn't have a full path, build one using getwd)
  784.     //
  785.     AppHome = NewCString(MAXPATHLEN);
  786.     strcpy(AppHome, NXArgv[0]);
  787.     if (AppHome[0] != '/')
  788.     {
  789.         //
  790.         //    We musta been started up from the command line, so use our working
  791.         //    directory to constructe the rest of the path.
  792.         //
  793.         temppath = NewCString(MAXPATHLEN);
  794.         getwd(temppath);
  795.         fullpath = NewCString(strlen(temppath) + 1 + strlen(AppHome));
  796.         sprintf(fullpath, "%s/%s", temppath, AppHome);
  797.         FreeCString(AppHome);
  798.         AppHome = fullpath;
  799.         FreeCString(temppath);
  800.     }
  801.     //
  802.     //    Now, remove the application's name from the end of the path.
  803.     //
  804.     tempPos = strrchr(AppHome, '/');
  805.     tempPos[0] = EndOfCString;
  806.     return self;
  807. }
  808.  
  809.  
  810. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  811. //    Routine:        free
  812. //    Parameters:    none
  813. //    Returns:        self
  814. //    Stores:        none
  815. //    Description:
  816. //        This merely cleans up after ourselves. 
  817. //    Bugs:
  818. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  819. - free
  820. {
  821.     FreeCString(AppHome);
  822.     return self;
  823. }
  824.  
  825. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  826. //    Routine:        GetBoolPref
  827. //    Parameters:    The preference that we are to retrieve the value for
  828. //    Returns:        YES or NO, depending on the value of the preference
  829. //    Stores:        None
  830. //    Description:
  831. //        This is used to lookup the value for a preference in the defaults database,
  832. //        and return a Boolean value to the caller.  It's really just provided as a
  833. //        shortcut to the several lines of code needed to retrieve the value, compare
  834. //        it to a string value of YES or NO, and then return the appropriate result.
  835. //    Bugs:
  836. //        If anything goes wrong, the user will get a NO value, and not an error code.
  837. //        Should I be disposing of the string that I get back?
  838. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  839. - (Boolean) GetBoolPref: (ConstCString) thePreference
  840. {
  841.     ConstCString    theValue;
  842.     Boolean    result;
  843.     
  844.     theValue = NXGetDefaultValue(DefaultsOwner, thePreference);
  845.     if (strcmp(theValue, "YES") == 0)
  846.         result = YES;
  847.     else
  848.         result = NO;
  849.     
  850.     return result;
  851. }
  852.  
  853.  
  854. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  855. //    Routine:        SetBoolPref:To:
  856. //    Parameters:    The preference that we are to retrieve the value for
  857. //                The Boolean value to assign it
  858. //    Returns:        self
  859. //    Stores:        None
  860. //    Description:
  861. //        This is used to store a boolean value for a preference in the defaults database.
  862. //    Bugs:
  863. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  864. - SetBoolPref: (ConstCString) thePreference To: (Boolean) value
  865. {
  866.     if (value == YES)
  867.         NXWriteDefault(DefaultsOwner, thePreference, "YES");
  868.     else
  869.         NXWriteDefault(DefaultsOwner, thePreference, "NO");
  870.     
  871.     return self;
  872. }
  873.  
  874.  
  875. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  876. //    Routine:        SetPref:To:
  877. //    Parameters:    The preference that we are to retrieve the value for
  878. //                The string value to assign it
  879. //    Returns:        self
  880. //    Stores:        None
  881. //    Description:
  882. //        This is used to store a string value for a preference in the defaults database.
  883. //    Bugs:
  884. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  885. - SetPref: (ConstCString) thePreference To: (CString) value
  886. {
  887.     NXWriteDefault(DefaultsOwner, thePreference, value);
  888.     return self;
  889. }
  890.  
  891. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  892. //    Routine:        GetPref:
  893. //    Parameters:    The preference that we are to retrieve the value for
  894. //    Returns:        A copy of the preference string.
  895. //    Stores:        None
  896. //    Description:
  897. //        This retrieves a string from the defaults database associated with the name
  898. //        thePreference, makes a copy of it, and returns it.
  899. //    Bugs:
  900. //    History:
  901. //        93.02.15    djb    Added for the rtf converter's new three option preference.
  902. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  903. - (CString) GetPref: (ConstCString) thePreference
  904. {
  905.     ConstCString    theValue;
  906.     CString            result;
  907.     
  908.     theValue = NXGetDefaultValue(DefaultsOwner, thePreference);
  909.  
  910.     result = NewCString(strlen(theValue));
  911.     strcpy(result, theValue);
  912.     
  913.     return result;
  914. }
  915.  
  916.  
  917.  
  918. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  919. //    Routine:        displayPreferences: 
  920. //    Parameters:    the object that called us
  921. //    Returns:        self
  922. //    Stores:        none
  923. //    Description:
  924. //        This method is indended to be overridden.  The subclass would just do whatever
  925. //        it needs to do to display preferences for the user..
  926. //    Bugs:
  927. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  928. -displayPreferences: target
  929. {
  930.     return self;
  931. }
  932.  
  933.  
  934.  
  935. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  936. //    Routine:        SetPercentageDone: 
  937. //    Parameters:    A new percentage to display
  938. //    Returns:        self
  939. //    Stores:        none
  940. //    Description:
  941. //        This allows a calling object to set the percentage of conversion done so far.
  942. //        Note that we only actually ask the percentage  meter to display itself in
  943. //        in increments of 5%, simply because re-drawing after every percent, for
  944. //        example, is EXTREMELY slow.
  945. //    Bugs:
  946. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  947. - SetPercentageDone: (Real) percentage
  948. {
  949.     if (percentage > lastPercent+5)
  950.     {
  951.         [ProgressMeter  SetTo: percentage];
  952.         lastPercent = percentage;
  953.         NXPing();
  954.     }
  955.     return self;
  956. }
  957.  
  958.  
  959.  
  960. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  961. //    Routine:        openSource and Dest files 
  962. //    Parameters:    the full path of the file to be converted.
  963. //    Returns:        the file object
  964. //    Stores:        errors
  965. //    Description:
  966. //        These tiny methods are provided so a subclass can override what kind of
  967. //        file gets opened for the source or the dest, without overriding the whole
  968. //        of the conversion methods.  Note that they should always open a subclass
  969. //        of File, though.
  970. //    Bugs:
  971. //    Modifications
  972. //        92.11.27    djb    Changed CreateAndOpenFor: to ClearAnd....  Any time this method
  973. //                    is called within the framework of this class, the user has already
  974. //                    specified they definitely want to use this file, even if it already
  975. //                    exists, so we want to just clear the file if it exists already.
  976. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  977. - openDestFile: (roCString) theFile
  978. {
  979.     Instance    fileInstance;
  980.     [self   ResetResults];
  981.     
  982.     fileInstance = [[File alloc] initAndUse: theFile];
  983.     if ([fileInstance   GetErrorCode] == ERR_OK)
  984.         [fileInstance   ClearAndOpenFor: FILE_WRITE];
  985.     if ([fileInstance   GetErrorCode] != ERR_OK)
  986.     {
  987.         [self   StoreErrorCode: ERR_CREATEFAILED
  988.             AndText: "We failed to create the file"];
  989.         [fileInstance   free];
  990.         fileInstance = NullInstance;
  991.     }
  992.     return fileInstance;
  993. }
  994.  
  995. - openSourceFile: (roCString) theFile
  996. {
  997.     Instance    fileInstance;
  998.     [self   ResetResults];
  999.     
  1000.     fileInstance = [[File alloc] initAndUse: theFile];
  1001.     if ([fileInstance   GetErrorCode] == ERR_OK)
  1002.         [fileInstance   OpenExistingFor: FILE_READ];
  1003.     if ([fileInstance   GetErrorCode] != ERR_OK)
  1004.     {
  1005.         [self   StoreErrorCode: ERR_OPENFAILED
  1006.             AndText: "We failed to open the file"];
  1007.         [fileInstance   free];
  1008.         fileInstance = NullInstance;
  1009.     }
  1010.     return fileInstance;
  1011. }
  1012.  
  1013.  
  1014.  
  1015. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1016. //    Routine:        closeSource and Dest files 
  1017. //    Parameters:    the file object to be closed
  1018. //    Returns:        self
  1019. //    Stores:        errors
  1020. //    Description:
  1021. //        These Little methods are used to undo the work of the above open methods.
  1022. //        they close the file objects, and free them.  Like the above, they are pretty
  1023. //        simple and generic, and will probably be overridden by many converters.
  1024. //        Note that the dest file closing method has a boolean flag.  If true, we also delete
  1025. //        the file (presumably something went wrong.
  1026. //    Bugs:
  1027. //    History
  1028. //        93.01.01    djb    Added tempString and freeing of it, so as not to leak memory.
  1029. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1030.  
  1031. - closeSourceFile: fileInstance
  1032. {
  1033.     CString    tempString    =  [fileInstance  GetErrorText];
  1034.     [self   ResetResults];
  1035.  
  1036.     [self   StoreErrorCode: [fileInstance  GetErrorCode]
  1037.         AndText: tempString];
  1038.     FreeCString(tempString);
  1039.     [fileInstance   free];
  1040.  
  1041.     return self;
  1042. }
  1043.  
  1044.  
  1045. - closeDestFile: fileInstance  andDelete: (Boolean) deleteit
  1046. {
  1047.     CString    tempString    =  [fileInstance  GetErrorText];
  1048.     [self   ResetResults];
  1049.  
  1050.     if (deleteit == NO)
  1051.         [fileInstance   CloseAndSave];
  1052.     else
  1053.         [fileInstance   CloseAndDelete];
  1054.     [self   StoreErrorCode: [fileInstance  GetErrorCode]
  1055.         AndText:  tempString];
  1056.     FreeCString(tempString);
  1057.     [fileInstance   free];
  1058.  
  1059.     return self;
  1060. }
  1061.  
  1062.  
  1063.  
  1064. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1065. //    Method:        preConversion
  1066. //    Parameters:    none
  1067. //    Returns:        self
  1068. //    Stores:        none
  1069. //    Description:
  1070. //        This method is called just before calling the conversion routine (i.e. after
  1071. //        files have been located and opened alright).  It does any work needed
  1072. //        to finish up preparing for the conversion.  At the moment, this means unregistering
  1073. //        the window, and setting the progress meter up.  One is welcome to subclass
  1074. //        this method, but should probably [super preConversion] before proceeding there.
  1075. //    Bugs:
  1076. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1077.  
  1078. - preConversion
  1079. {
  1080.     [self   refuseDragAndDrop];
  1081.     lastPercent = 0;
  1082.     [ProgressMeter   ActivateWithGoal: 100];
  1083.     return self;
  1084. }
  1085.  
  1086.  
  1087. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1088. //    Method:        postConversion
  1089. //    Parameters:    none
  1090. //    Returns:        self
  1091. //    Stores:        none
  1092. //    Description:
  1093. //        This method is called just after calling the conversion routine has finished.
  1094. //        It does some work needed to clean up aspects of this instance after the conversion.
  1095. //        At the moment, this means re-registering the window for file-icon-dropping,
  1096. //        and making sure the progress meter shows 100% done.  One is welcome to subclass
  1097. //        this method, but should probably [super postConversion] before proceeding there.
  1098. //    Bugs:
  1099. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1100.  
  1101. - postConversion
  1102. {
  1103.     [self   allowDragAndDrop];
  1104.     [ProgressMeter  SetTo: 100];
  1105.     [ProgressMeter   Deactivate];
  1106.     return self;
  1107. }
  1108.  
  1109.  
  1110. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1111. //    Method:        CheckFileOK
  1112. //    Parameters:    none
  1113. //    Returns:        self
  1114. //    Stores:        none
  1115. //    Description:
  1116. //        This asks the converter if this is an ok file to be converted.  If no, then it
  1117. //        gets the reason why, displays it for the user, and goes into a modal loop to
  1118. //        force the user to decide whether to continue or not.  If the user says continue,
  1119. //        we continue.
  1120. //    Bugs:
  1121. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1122. - (Boolean) CheckFileOK: fileInst
  1123. {
  1124.     cancelResult = NO;
  1125.     if ( [converterInst   respondsTo:@selector(isThisAGoodFile:)] ) 
  1126.     {
  1127.         cancelResult = [converterInst   isThisAGoodFile: fileInst];
  1128.         if (cancelResult != YES)
  1129.         {
  1130.             [decisionText    setStringValue: [converterInst   GetCStringFrom: SECOND_RESULT]];
  1131.             [decisionWindow   center];
  1132.             [decisionWindow   makeKeyAndOrderFront: self];
  1133.             [NXApp   runModalFor: decisionWindow];
  1134.         }
  1135.     }
  1136.     else
  1137.         cancelResult = YES;
  1138.     return cancelResult;
  1139. }
  1140.  
  1141.  
  1142. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1143. //    Method:        UserDecided
  1144. //    Parameters:    the thing that called us
  1145. //    Returns:        self
  1146. //    Stores:        none
  1147. //    Description:
  1148. //        This method gets called when the user clicks on a button in a window during
  1149. //        the modal loop set up in CheckFileOK, above.  It gets the result and stores it in
  1150. //        cancelResult, closes the window and shuts down the modal loop.
  1151. //    Bugs:
  1152. //        I dislike modality in general.
  1153. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1154. - UserDecided: sender
  1155. {
  1156.     if ( [sender   tag] == 0)
  1157.         cancelResult = NO;
  1158.     else
  1159.         cancelResult = YES;
  1160.     [decisionWindow   close];
  1161.     [NXApp   stopModal];
  1162.     return self;
  1163. }
  1164.  
  1165.  
  1166.  
  1167. /*============================================================*\
  1168.  
  1169.     At last, here are the true guts of the application.   The following three methods
  1170.     deal with processing files.  Briefly, PrepareForConversion: starts from scratch,
  1171.     and prompts the user for a file to convert from.  ConvertThisFile: takes a file name
  1172.     and determines what file to put the conversion results into.  ConvertFrom:To:
  1173.     then just converts between the two files (this last and init are things that
  1174.     subclasses MUST override.
  1175.  
  1176. \*============================================================*/
  1177.  
  1178.  
  1179. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1180. //    Method:        PrepareForConversion:
  1181. //    Parameters:    the object that send this message
  1182. //    Returns:        self
  1183. //    Stores:        error code
  1184. //    Description:
  1185. //        This merely creates the converstion necessary object, and sets its
  1186. //        preference for using curly quotes according to the value stored in this
  1187. //        object.  Then, call the method to start getting information from the user to
  1188. //        do the conversion.
  1189. //    Bugs:
  1190. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1191. - PrepareForConversion: sender
  1192. {
  1193.     Instance            theOpenPanel = [OpenPanel new];
  1194.     CString const *    openedFiles;
  1195.     roCString        theDirectory;
  1196.     CString            fullPath, theFile;
  1197.     CString            tempString;
  1198.     Integer            result, pathLength, index;
  1199.  
  1200.     [self  ResetResults];
  1201.     //
  1202.     //    Set up the  display fields
  1203.     //
  1204.     [SourceFileName    setStringValue: ""];
  1205.     [SourcePath    setStringValue: ""];
  1206.     [SourceTitle    setStringValue: "Converting:"];
  1207.     [SourcePathTitle    setStringValue: "In:"];
  1208.  
  1209.     [DestFileName    setStringValue: ""];
  1210.     [DestPath    setStringValue: ""];
  1211.     [DestTitle    setStringValue: "To:"];
  1212.     [DestPathTitle    setStringValue: "In:"];
  1213.     
  1214.     [StatusTitle    setStringValue: "Status:"];
  1215.     [Status    setStringValue: ConversionString];
  1216.     //
  1217.     //    Prepare the open dialog, and get info from the user.
  1218.     //    @@ Bug: it doesn't let the user know what kind of conversion
  1219.     //    @@ process is now underway in this dialog
  1220.     //
  1221.     [cutCommand    setEnabled: YES];
  1222.     [pasteCommand    setEnabled: YES];
  1223.     [spellingCommand    setEnabled: YES];
  1224.     [checkSpellingCommand    setEnabled: YES];
  1225.     
  1226.     [theOpenPanel allowMultipleFiles: YES];
  1227.     [theOpenPanel setPrompt: SourcePrompt]; // use instance variable for prompt
  1228.     [theOpenPanel setTitle: "Convert"];
  1229.     result = [theOpenPanel runModal];
  1230.  
  1231.     [cutCommand    setEnabled: NO];
  1232.     [pasteCommand    setEnabled: NO];
  1233.     [spellingCommand    setEnabled: NO];
  1234.     [checkSpellingCommand    setEnabled: NO];
  1235.  
  1236.     if (result != 1)
  1237.     {
  1238.         [self StoreErrorCode:  ERR_USERABORTED
  1239.             AndText: "User canceled when opening a source file!"];
  1240.         [Status    setStringValue: "Conversion process canceled at your request!"];
  1241.     }
  1242.     else
  1243.     {
  1244.         //
  1245.         //    get the set of files, as well as the path to them (or it)
  1246.         //
  1247.         openedFiles = [theOpenPanel filenames];
  1248.         theDirectory = [theOpenPanel directory];
  1249.         pathLength = strlen(theDirectory);
  1250.         //
  1251.         //    For each of the one or more files, allocate a space for the full path.
  1252.         //    construct the path using the directory name and file name.
  1253.         //    then, call the method to finish up, and dispose of the path we built.
  1254.         //    Note: re: the if-then-else to fuse the file name and path
  1255.         //    If the directory path is not empty, and it doesn't end in a slash, then
  1256.         //    we build the full path from dir/file.  Otherwise, do the same without a
  1257.         //    slash.  This last is usefull if someone has chosen a file at the root and we
  1258.         //    don't want a double slash (i.e. //myfile). (the check for zero length is really
  1259.         //    just for completeness...)
  1260.         //
  1261.         for (index=0;  openedFiles[index] != NULL; index++)
  1262.         {
  1263.             theFile = openedFiles[index];
  1264.             fullPath = NewCString(strlen(theFile) + 1 + pathLength);
  1265.             if ((pathLength > 0) && (theDirectory[pathLength-1] != '/'))
  1266.                 sprintf(fullPath, "%s/%s", theDirectory, theFile);
  1267.             else
  1268.                 sprintf(fullPath, "%s%s", theDirectory, theFile);
  1269.             //
  1270.             //    With a path in hand, do the next stage in the converstion.
  1271.             //
  1272.             [self ConvertThisFile: fullPath];
  1273.             FreeCString(fullPath);
  1274.         }
  1275.         //
  1276.         //    Check if the file was converted alright...
  1277.         //
  1278.         if ([self   GetErrorCode] == ERR_OK)
  1279.         {
  1280.             [SourceTitle    setStringValue: "Converted:"];
  1281.             [Status    setStringValue: "The conversion is done."];
  1282.             [self StoreErrorCode:  ERR_OK AndText: "Conversion process done"];
  1283.         }
  1284.         else
  1285.         {
  1286.             [SourceTitle    setStringValue: "Failed:"];
  1287.             tempString = [self   GetErrorText];
  1288.             [Status    setStringValue: tempString];
  1289.             FreeCString(tempString);
  1290.         }
  1291.     }
  1292.     return self;
  1293. }
  1294.  
  1295.  
  1296.  
  1297.  
  1298.  
  1299. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1300. //    Routine: ConvertThisFile 
  1301. //    Parameters: the full path of the file to be converted.
  1302. //    Returns:        self
  1303. //    Stores:        error code
  1304. //    Description:
  1305. //        Given a file, you want to convert it!  First, you make sure you can open the file OK,
  1306. //        then, you ask the user for what the destination file should be called.  Having done
  1307. //        this, you then assure that the destination file can be created without problems,
  1308. //        and then you ask the converting routine to convert between the two..
  1309. //    Bugs:
  1310. //        I find this routine a bit too confusing for my tastes...  (long-winded)
  1311. //        We retrieve some strings below, but can't free them.
  1312. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1313. - ConvertThisFile: (roCString) theFile
  1314. {
  1315.     roCString    destname;
  1316.     Instance        sourceFile, destFile;
  1317.     Integer        outcome;
  1318.     PositiveInteger    extenLen, destLen;
  1319.     CString        filename, directory, tempstring, basename;
  1320.     Instance        theSavePanel;
  1321.     Boolean        goodfile;
  1322.     CString        tempString;
  1323.     //
  1324.     [self ResetResults];
  1325.     //
  1326.     //    Set up the  display fields (we don't always go through prepare for conversion, above.
  1327.     //
  1328.     [SourceFileName    setStringValue: ""];
  1329.     [SourcePath    setStringValue: ""];
  1330.     [SourceTitle    setStringValue: "Converting:"];
  1331.     [SourcePathTitle    setStringValue: "In:"];
  1332.  
  1333.     [DestFileName    setStringValue: ""];
  1334.     [DestPath    setStringValue: ""];
  1335.     [DestTitle    setStringValue: "To:"];
  1336.     [DestPathTitle    setStringValue: "In:"];
  1337.     
  1338.     [StatusTitle    setStringValue: "Status:"];
  1339.     [Status    setStringValue: ConversionString];
  1340.     //
  1341.     //    If possible,, open the source file, and update the display. 
  1342.     //    
  1343.     sourceFile = [self openSourceFile: theFile];
  1344.     if ([self   GetErrorCode] != ERR_OK)
  1345.     {
  1346.         [SourceFileName    setStringValue: theFile];
  1347.         [SourcePath    setStringValue: ""];
  1348.         tempString = [self   GetErrorText];
  1349.         [Status    setStringValue: tempString];
  1350.         FreeCString(tempString);
  1351.     }
  1352.     else
  1353.     {
  1354.         goodfile = [self  CheckFileOK: sourceFile];
  1355.         if (goodfile == NO)
  1356.         {
  1357.             filename = [sourceFile GetFilename];
  1358.             directory = [sourceFile GetDirectory];
  1359.             [SourceFileName    setStringValue: filename];
  1360.             [SourcePath        setStringValue: directory];
  1361.             FreeCString (filename);
  1362.             FreeCString (directory);
  1363.             [Status    setStringValue: "The conversion was canceled at your request"];
  1364.             [self   StoreErrorCode: ERR_USERABORTED  AndText: "The conversion was canceled at your request"];
  1365.         }
  1366.         else
  1367.         {
  1368.             ////
  1369.             //    Get descriptions of the file's name, and use these to build a message
  1370.             //    on the screen for the user, and a prompt in the save panel
  1371.             ////
  1372.             filename = [sourceFile GetFilename];
  1373.             directory = [sourceFile GetDirectory];
  1374.             basename = [sourceFile GetBasename];
  1375.             //
  1376.             //    Update the user about current events.
  1377.             //
  1378.             [SourceFileName    setStringValue: filename];
  1379.             [SourcePath    setStringValue: directory];
  1380.             [Status    setStringValue:  "OK"];
  1381.             //
  1382.             //    Then, build a name for the file we want to convert to
  1383.             //
  1384.             [cutCommand    setEnabled: YES];
  1385.             [pasteCommand    setEnabled: YES];
  1386.             [spellingCommand    setEnabled: YES];
  1387.             [checkSpellingCommand    setEnabled: YES];
  1388.  
  1389.             theSavePanel = [SavePanel   new];
  1390.             [theSavePanel setPrompt: DestPrompt];
  1391.             [theSavePanel setTitle: "Convert to"];
  1392.             tempstring = NewCString(strlen(basename) + strlen(DestExtension));
  1393.             sprintf(tempstring, "%s%s", basename, DestExtension);
  1394.             //    93.01.01    djb    Added this to free the leak with basename
  1395.             FreeCString(basename);
  1396.             outcome = [theSavePanel runModalForDirectory: directory file: tempstring];
  1397.             
  1398.             FreeCString (tempstring);
  1399.             FreeCString (filename);
  1400.             FreeCString (directory);
  1401.  
  1402.             [cutCommand    setEnabled: NO];
  1403.             [pasteCommand    setEnabled: NO];
  1404.             [spellingCommand    setEnabled: NO];
  1405.             [checkSpellingCommand    setEnabled: NO];
  1406.  
  1407.             ////
  1408.             //    Now, check the output of the call to runModalForDirectory:file.  If
  1409.             //    the user canceled, tell the user this.  Otherwise, continue processing.
  1410.             ////
  1411.             if (outcome != 1)
  1412.             {
  1413.                 [self StoreErrorCode:  ERR_USERABORTED
  1414.                     AndText: "Conversion process canceled at your request"];
  1415.                 [SourceTitle    setStringValue: "Canceled:"];
  1416.                 [Status    setStringValue:  "Conversion process canceled at your request!"];
  1417.             }
  1418.             else
  1419.             {
  1420.                 ////
  1421.                 //    Open the destination file.  If we can't, display why, otherwise show the
  1422.                 //    user what the destination file name and path is.   Finally, CONVERT
  1423.                 //    the bloody file!
  1424.                 ////
  1425.                 //
  1426.                 //    See if the file ends with the proper extension.  If so, copy it into a
  1427.                 //    temporary string.  Otherwise, tack the extension onto it...
  1428.                 //
  1429.                 destname = [theSavePanel filename];
  1430.                 extenLen = strlen(DestExtension);
  1431.                 destLen = strlen(destname);
  1432.                 if (strcmp(&destname[destLen - extenLen], DestExtension) == 0)
  1433.                 {
  1434.                     tempstring = NewCString(destLen);
  1435.                     strcpy(tempstring, destname);
  1436.                 }
  1437.                 else
  1438.                 {
  1439.                     tempstring = NewCString(destLen+extenLen);
  1440.                     sprintf(tempstring, "%s%s", destname, DestExtension);
  1441.                 }
  1442.                 //
  1443.                 //    Make sure that this ne name doesn't refer to the source file
  1444.                 //
  1445.                 if ([sourceFile      SameFileAs:  tempstring] == YES) 
  1446.                 {
  1447.                     [SourceTitle    setStringValue: "Error:"];
  1448.                     [Status    setStringValue:  "These refer to the same file.  This is not allowed!" ];
  1449.     
  1450.                     [self StoreErrorCode:  ERR_TRIEDTOOVERWRITE
  1451.                         AndText: "Attempt to overwrite source file with destination file! Conversion NOT done."];
  1452.                     [sourceFile    Close];
  1453.                     FreeCString(tempstring);
  1454.                 }
  1455.                 else
  1456.                 {
  1457.                     destFile = [self openDestFile: tempstring];
  1458.                     FreeCString(tempstring);
  1459.                     if ([self    GetErrorCode] != ERR_OK)
  1460.                     {
  1461.                         [SourceTitle    setStringValue: "Failed:"];
  1462.                         tempString = [self   GetErrorText];
  1463.                         [Status    setStringValue: tempString];
  1464.                         FreeCString(tempString);
  1465.                         [sourceFile    Close];
  1466.                     }
  1467.                     else
  1468.                     {
  1469.                         //
  1470.                         //    we opened the dest file.  So, tell the user, and continue.
  1471.                         //
  1472.                         filename = [destFile GetFilename];
  1473.                         directory = [destFile GetDirectory];
  1474.                         [DestFileName    setStringValue: filename];
  1475.                         [DestPath        setStringValue: directory];
  1476.                         FreeCString (filename);
  1477.                         FreeCString (directory);
  1478.                         [self  DoConversionFrom: sourceFile  To: destFile];
  1479.                     }
  1480.                 }
  1481.             }
  1482.         }
  1483.     }
  1484.  
  1485.     return self;
  1486. }
  1487.  
  1488.  
  1489. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1490. //    Routine:        DoConversionFrom:To: 
  1491. //    Parameters:    The file to be converted from, and the file to be converted to
  1492. //    Returns:        self
  1493. //    Stores:        none
  1494. //    Description:
  1495. //        This is called once we have a source and destination file object opened.
  1496. //        It does any preparation needed by the application, then calls the convertFrom:To:
  1497. //        method, and cleans up afterwards.
  1498. //        Note: tempInst is a kludge that was added for things like rtfConverter which
  1499. //        destroys the destination file and creates a new one.  This allows us to deal with
  1500. //        this situation.
  1501. //    Bugs
  1502. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1503. - DoConversionFrom: sourceFile  To: destinationFile
  1504. {
  1505.     Instance        tempInst;
  1506.     Integer        tempError;
  1507.     CString        errorText;
  1508.     //
  1509.     //    Do some preparing of the conversion window...
  1510.     //
  1511.     [self   preConversion];
  1512.     //
  1513.     //    Dim the menus during any conversion process
  1514.     //
  1515.     [quitCommand    setEnabled: NO];
  1516.     [hideCommand    setEnabled: NO];
  1517.     [infoCommands    setEnabled: NO];
  1518.     [editCommands    setEnabled: NO];
  1519.     [servicesCommands setEnabled: NO];
  1520.     [windowsCommands setEnabled: NO];
  1521.  
  1522.     [Status    setStringValue:  "Converting"];
  1523.     tempInst = [self ConvertFrom: sourceFile To: destinationFile];
  1524.     if (destIsDead == YES)
  1525.         destinationFile = tempInst;
  1526.     destIsDead = NO;
  1527.     [quitCommand    setEnabled: YES];
  1528.     [hideCommand    setEnabled: YES];
  1529.     [infoCommands    setEnabled: YES];
  1530.     [editCommands    setEnabled: YES];
  1531.     [servicesCommands setEnabled: YES];
  1532.     [windowsCommands setEnabled: YES];
  1533.     //
  1534.     //    Close the files, now that we are done converting.
  1535.     //
  1536.     tempError = [self GetErrorCode];
  1537.     errorText = [self GetErrorText];
  1538.     if (tempError < ERR_OK)
  1539.         [self   closeDestFile: destinationFile  andDelete: YES];
  1540.     else
  1541.         [self   closeDestFile: destinationFile  andDelete: NO];
  1542.     [self   closeSourceFile: sourceFile];
  1543.  
  1544.     [self   postConversion];
  1545.  
  1546.     [self   StoreErrorCode: tempError AndText: errorText];
  1547.     FreeCString(errorText);
  1548.     return self;
  1549. }
  1550.  
  1551.  
  1552. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1553. //    Routine:        ConvertFrom:To: 
  1554. //    Parameters:    The file to be converted from, and the file to be converted to
  1555. //    Returns:        self
  1556. //    Stores:        none
  1557. //    Description:
  1558. //        This is a method indended to be subclassed.  It would do the actual conversion
  1559. //        of the source to destination file.
  1560. //    Bugs
  1561. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1562. - ConvertFrom: sourceFile To: destinationFile
  1563. {
  1564.     //
  1565.     //    If our converter instance has the ReportTo: method, then we tell it to report to it
  1566.     //
  1567.     if ( [converterInst respondsTo:@selector(ReportTo:)] ) 
  1568.         [converterInst   ReportTo: self];
  1569.     [Status    setStringValue:  "Converting"];
  1570.     return self;
  1571. }
  1572.  
  1573. @end
  1574.